import sys, os
import re, base64, hashlib, random, datetime
import web

import log 
from cli import Command
from repo import Repository, RepoCommand


urls = (
	r'^/simple/', 'simple',
	r'^/simple/(?P<dist_name>[\w\d_\.\-]+)/(?P<version>[\w\.\d\-_]+)/$', 'show_version',
	r'^/simple/(?P<dist_name>[\w\d_\.\-]+)/$', 'show_links',
	r'^/$', "simple",
	r'^/(?P<dist_name>[\w\d_\.\-]+)/$', 'show_links',
	r'^/search', 'search'
)


def require_login(function):
	def wrapper(*args, **kwargs):
		auth = web.ctx.env.get('HTTP_AUTHORIZATION')
		
		if not auth is None:
			auth = re.sub('^Basic ', '', auth)
			user_name, password = base64.decodestring(auth).split(':')
			if user_name:
				try:
					web.ctx.user_id = web.ctx.repo.authenticate(user_name, password)
					return function(*args, **kwargs)
				except web.ctx.repo.AuthenticationError as error:
					log.warning('auth: %s', error)
		
		raise web.webapi.Unauthorized(data='Unauthorized', headers = {'WWW-Authenticate' : 'Basic realm="{0}"'.format(web.ctx.repo.title)})
	return wrapper


class simple(object):
	def GET(self):
		web.header('Content-Type','text/html; charset=utf-8')
		return web.template.Template(
			'''<html><head><title>Simple Index</title></head><body>
			$for project in projects:
				<a href='/$project.name/'>$project.name</a><br/>
			</body></html>''',
			globals = dict(
				projects = web.ctx.repo.list_projects())
		)()

	@require_login
	def submit_project_or_release(self, data):
		repo = web.ctx.repo
		
		repo.db.transaction()
		
		try:
			project = repo.get_project(data.name, ('id', 'name', 'owner_id'))
		except ObjectDoesNotExist:
			project = None
		
		if project and project.owner_id != web.ctx.user_id:
			raise web.webapi.Forbidden('Project {0} is owned by someone else'.format(project.name))
		
		project_id = repo.add_or_update_project(
			project.id if project else None,
			web.ctx.user_id,
			data.name,
			data.license,
			data.metadata_version,
			data.author,
			data.home_page,
			data.download_url,
			data.summary,
			data.description,
			data.author_email,
			repo.get_or_create_classifiers(data.classifiers)
		)
		try:
			repo.add_or_update_release(project_id, 
				data.version, data.content.filename, data.md5_digest, data.platform, 
				data.get('signature',''), data.filetype, data.pyversion, data.content.file)
		except repo.SubmissionError as error:
			raise web.webapi.Forbidden(error)
	
	def create_user(self):
		repo = web.ctx.repo
		
		if repo.self_registration:
			raise web.webapi.Forbidden()
		
		data = web.webapi.input()
				
		password = data.get('password1', None)
		if password and (password == data.get('password2', None)):
			repo.add_user(data.get('name', None), False, data.get('email', None), password)
			return
		
		raise web.webapi.BadRequest()
	
	def POST(self):
		data = web.webapi.input(content = {}, classifiers = [])
		action = data.get(':action', None)
		
		if action == 'file_upload' or action == 'submit':
			self.submit_project_or_release(data)
		elif action == 'user':
			pass
		else:
			raise web.webapi.HTTPError('501 The action {0} is not implemented'.format(action))


class show_version(object):
	def GET(self, dist_name, version):
		repo = web.ctx.repo
		try:
			project = repo.get_project(dist_name, ('id', 'name'))
			releases = repo.get_project_releases(project.id, version = version)
		except repo.DoesNotExist:
			raise web.webapi.NotFound()
		
		web.header('Content-Type','text/html; charset=utf-8')
		return web.template.Template(
'''<html><head><title>Links for $project.name $version</title></head><body>
<h1>Links for $project.name $version</h1>
$for release in releases:
	<a href="$repo.distr_download_link(release.filename)">$release.filename</a><br/>
</body></html>''',
			globals = dict(
				project = project, version = version, releases = releases, repo = repo
			)
		)

class show_links(object):
	def GET(self, dist_name):
		repo = web.ctx.repo
		try:
			project = repo.get_project(dist_name, ('id', 'name'))
		except repo.DoesNotExist:
			raise web.webapi.NotFound()
		
		web.header('Content-Type','text/html; charset=utf-8')
		return web.template.Template(
'''<html>
<head>
<title>Links for $project.name</title>
</head>
<body>
<h1>Links for $project.name</h1>
$for release in releases:
	<a href="$repo.distr_download_link(release)">$release.filename</a><br/>
</body>
</html>''',
			globals = dict(
				project = project, releases = repo.get_project_releases(project.id), repo = repo
			)		
		)()


class search(object):
	results_template = '''<html><head><title>Search Results</title></head><body>
$if search_term:
	<h1>Index of Packages Matching '$search_term'</h1>
$else:
	<h1>You need to supply a search term.</h1>

$if projects:
	<table>
		<thead><th>Updated</th><th>Package</th><th>Summary</th>
		</thead>
		<tbody>
	$for project in projects:
		<tr>
			<td>$project.updated</td>
			<td><a href="/$project.name/"/>$project.name</a></td>
			<td>$project.summary</td>
		</tr>
	</tbody>
	</table>
$else:
	There were no matches.
</body></html>
'''
	def GET(self):
		repo = web.ctx.repo
		data = web.webapi.input()
		
		projects = repo.search_projects(data.search_term, ('name', 'summary', 'updated'))
		
		data = web.webapi.input()
		
		web.header('Content-Type','text/html; charset=utf-8')
		return web.template.Template(self.results_template,
			globals = dict(projects = projects, repo = repo, search_term = data.search_term))()


@Command.register('simple-serve')
class SimpleServeCommand(RepoCommand):
	def add_arguments(self, parser):
		super(SimpleServeCommand, self).add_arguments(parser)
		parser.add_argument('--title', default='webpyshop repository')
		parser.add_argument('--self-registration', action='store_true', default=False)
		parser.add_argument('--release-overwrites', action='store_true', default=False)
		parser.add_argument('--listen', default='localhost:8000')
	
	def execute(self, args):
		super(SimpleServeCommand, self).execute(args)
		
		def processor(self, handler):
			web.ctx.repo = self
			return handler()

		app = web.application(urls, globals())
		app.add_processor(repo.processor)
		web.httpserver.runsimple(app.wsgifunc(), web.wsgi.validip(options.listen))
